#ifndef _NE_TEXTURE_H_
#define _NE_TEXTURE_H_

#include <graphics/GLObject.h>
#include <graphics/Image.h>

namespace ne
{
    class NEAPI Texture : public Resource, public GLObject
    {
        public:
            Texture(ResourceManager *pManager,
                    const uint64_t id,
                    const std::string &name,
                    const std::string &fileName,
                    const bool isManual,
                    ManualResourceLoader *pLoader);

            void createInternalTexture(const TextureType type,
                                       const TextureFormat internalFormat,
                                       const PixelFormat format,
                                       const PixelType pt,
                                       const uint32_t width,
                                       const uint32_t height,
                                       const uint32_t depth=1,
                                       const void *data=0);

            void freeInternalTexture();

            void createRenderTexture();

            void loadImage(const Image &image);

            void loadData(const void *data,
                          const PixelFormat format,
                          const PixelType pt,
                          const uint32_t x,
                          const uint32_t y,
                          const uint32_t z,
                          const uint32_t width,
                          const uint32_t height,
                          const uint32_t depth=1);

            void loadImageForCubeMap(const TextureCubeMapFace face,
                                     const Image &image);

            void loadDataForCubeMap(const TextureCubeMapFace face,
                                    const void *data,
                                    const PixelFormat format,
                                    const PixelType pt,
                                    const uint32_t width,
                                    const uint32_t height);

            //TODO: Only can call this before createInternalTexture
            void setType(const TextureType type);
            TextureType getType() const;

            //TODO: Only can call this before createInternalTexture
            void setFormat(const TextureFormat format);
            TextureFormat getFormat() const;

            PixelType getPixelType() const;

            uint32_t getWidth() const;

            uint32_t getHeight() const;

            uint32_t getDepth() const;

            void setNumMipmap(const uint32_t mip);

            uint32_t getNumMipmap() const;

            void setFilter(const TextureFilterFunc minFF, const TextureFilterFunc magFF);

            TextureFilterFunc getMinFilter() const;

            TextureFilterFunc getMagFilter() const;

            void setLOD(const real minLOD, const real maxLOD, const real bias);

            real getMinLOD() const;

            real getMaxLOD() const;

            real getLODBias() const;

            void setWrap(const TextureWrapParameter wpS,
                         const TextureWrapParameter wpT=TWP_CLAMP_TO_EDGE,
                         const TextureWrapParameter wpR=TWP_CLAMP_TO_EDGE);

            TextureWrapParameter getWrapS() const;

            TextureWrapParameter getWrapT() const;

            TextureWrapParameter getWrapR() const;

            void setDepthCompare(const bool enable=true,
                                 const TextureCompareFunc cf=TCF_LEQUAL,
                                 const TextureDepthTextureMode dtm=TDTM_LUMINANCE);

            TextureCompareMode getCompareMode() const;

            TextureCompareFunc getCompareFunc() const;

            TextureDepthTextureMode getDepthTextureMode() const;

            bool isCreated() const;

            virtual ~Texture();

        protected:
            virtual bool loadImplement();

            virtual bool unloadImplement();

            virtual bool downloadImplement();

            virtual size_t _calculateSize() const;

            void _needCreated();

        private:
            TextureType mType;
            TextureFormat mFormat;
            PixelType mPixelType;

            // OpenGL 2.1  2n + 2(border)
            uint32_t mWidth;
            uint32_t mHeight;
            uint32_t mDepth;

            uint32_t mNumMipmap; // any non-negative integer
            TextureFilterFunc mMinFilter;
            TextureFilterFunc mMagFilter;
            real mMinLOD;   // -1000
            real mMaxLOD;   // +1000
            real mLODBias;
            TextureWrapParameter mWrapS;
            TextureWrapParameter mWrapT;
            TextureWrapParameter mWrapR;

            TextureCompareMode mCompareMode;
            TextureCompareFunc mCompareFunc;
            TextureDepthTextureMode mDepthTextureMode;

            bool mCreated;  // Render texture created
    };

    class NEAPI TexturePtr : public SharedPtr<Texture>
    {
        public:
            TexturePtr() : SharedPtr<Texture>() {}
            explicit TexturePtr(Texture *pTexture) : SharedPtr<Texture>(pTexture) {}
            TexturePtr(const TexturePtr &pTexture) : SharedPtr<Texture>(pTexture) {}

            TexturePtr(const ResourcePtr &p)
                : SharedPtr<Texture>()
            {
                mpPtr = static_cast<Texture*>(p.ptr());
                mpUseCount = p.getUseCountPtr();
                if (mpUseCount)
                {
                    ++(*mpUseCount);
                }
            }

            TexturePtr& operator = (const ResourcePtr &p)
            {
                if (mpPtr == static_cast<Texture*>(p.ptr()))
                {
                    return *this;
                }
                TexturePtr temp(p);
                swap(temp);
                return *this;
            }
    };

    typedef std::map<std::string, TexturePtr> TextureMap;
    typedef TextureMap::iterator TextureMapIterator;
    typedef TextureMap::const_iterator TextureMapConstIterator;
}

#endif
